查看原文
其他

【第2251期】Matomo 从了解到落地——页面流量统计与分析最佳实践

Kayo 前端早读课 2021-04-12

前言

又是一个可以私有化部署的工具。今日前端早读课文章由腾讯@Kayo投稿分享。

@李浩成 Kayo,2014 年加入腾讯,现任微信读书高级 UI 工程师,目前承担微信读书/微信听书的 Web 端相关业务,拥有丰富的 Web 全栈研发经验。

正文从这开始~~

背景

在开发面向内部使用的「内容管理平台」的过程中,我们不时会收到一些页面问题的反馈,但在本地调试的过程中,有大量无法在本地重现的问题,这些问题的出现跟用户的访问设备、网络环境、访问路径可能存在关联。为了方便快捷地去定位这些问题,我们试图为所有页面点击操作都加上打点记录,但在实际操作中,由于业务变更频繁,开发框架的限制,展示打点数据较为复杂等因素,通过打点排查问题的实际效果并不理想,因此我们希望引入完整的流量统计和用户行为分析来定位问题。

不同的方案分析对比

对于流量统计和用户行为分析记录的工具,行业内已经有大量成熟的解决方案,相对于自行打点,这些专门的流量通过平台和工具对于业务的基本没有侵入性,也解决了如何展示数据的问题。这些平台和工具中,有著名的 Google Analytics、百度统计、WebTrends 等,也有相对冷门的今天的主角 —— Matomo,而这些方案之间各有优劣:

通过对比,Matomo 整体功能比较强大,对标了 Google Analytics 但在安全性和私密性方面更优,支持私有化部署,代码和数据都可以不透露给第三方,并且可以通过插件的机制配合业务实现自定义,这些优点都是我们最终选择 Matomo 作为「内容管理平台」用户记录的工具的原因。

Matomo 是什么?

这里介绍一下 Matomo,作为一套基于 PHP 与 MySQL 的网页流量统计和分析平台,它的大部分功能已经开源,并且做了很好的封装,可以轻松地进行私有化部署,它的功能主要分成两块:

  • 收集并存储页面访问数据,主要是用户信息,如设备型号、分辨率、用户地区、来源,以及页面信息,如页面访问路径、访问操作等。

  • 对收集起来的数据进行指标量化并可视化的展示,例如用户设备型号分布、地区分布、某个页面的浏览人数、访问最多的页面、某个用户在某个页面的访问路径和具体操作等,并且在收集数据时,Matomo 会有大量的策略保护用户隐私,例如上报 IP 时隐藏最后一位字节等。

在实际使用时,用户信息的上报以及页面的访问路径,只需要安装并引入 Matomo 即可实现,无需额外的配置。但是开发者可以通过接口增强上报的数据,例如上报某个弹窗的展示,或者上报某个请求的结果,这样最终可以在平台上展示出完整的用户访问路径和操作,结合业务日志,可以很准确地定位问题以及还原问题的触发路径。

Matomo 落地到业务

在引入 Matomo 之前,先说明一下 Matomo 的主要组成追踪器和 Matomo 服务端,追踪器基于 JS 实现,需要在网页引入,用于上报数据。服务端主要提供了三个功能:

  • HTTP 接口,追踪器可以收集所在网页的数据但不上报,通过 HTTP 接口发送给 Matomo。

  • 归档任务运行并预处理数据,默认分为实时动态处理(页面访问数据,用户访问轨迹)和 cron 任务处理(用户维度的列表)。

  • 可视化展现数据,也可以数据接口或者报表接口来访问这些数据。

引入简单落地不易

Matomo 有很成熟封装,因此本身部署很简单,主要分为两个步骤:

  • 部署私有化 Matomo 服务。

  • 在需要流量统计ide页面上引入追踪器。

其中部署私有化服务只需要下载 Matomo 的程序并上传到服务端,然后打开访问地址就可以使用引导程序部署服务,包括检测服务器环境是否符合要求,填写数据库信息,创建管理账号等,具体参考官方文档。

但在实际落地到内容平台的过程中,却遇到了问题——我们需要基于 Docker 进行部署。

由于业务的部署都基于 Docker 和 k8s 进行,因此私有化的 Matomo 也需要基于此进行部署,这样会带来几个问题:

  • Matomo 的设置分成系统配置与功能设置,其中功能设置储存在 MySQL 中,而系统设置则储存在本地的配置文件中,当部署多个容器时,配置无法对齐,另外 Docker 重新部署后,这些配置修改也会丢失。

  • 这套部署需要域名 + 路径的形式访问 Matomo,Matomo 社区镜像中是使用 Apache2 进行路由处理的,而 Apache2 默认的配置并不适配路径,需要修改 Apache 的配置文件。

解决在 Docker 中部署 Matomo 的问题

Matomo 有官方发布的社区镜像可以直接使用,但为了解决上述的问题,需要在构建 Docker 镜像时进行额外的处理。

解决配置丢失的问题

Matomo 的配置文件是 config/config.ini.php,不跟随版本管理,为了获取一份默认的配置文件,可以用社区镜像预先部署好一个 Matomo 容器,并在容器中获取一份默认的配置文件,例如:

  1. [database]

  2. host = "${MATOMO_DATABASE_HOST}"

  3. username = "${MATOMO_DATABASE_USERNAME}"

  4. password = "${MATOMO_DATABASE_PASSWORD}"

  5. dbname = "${MATOMO_DATABASE_DBNAME}"

  6. tables_prefix = "${MATOMO_DATABASE_TABLES_PREFIX}"

  7. charset = "utf8mb4"

  8. multi_server_environment = 1

  9. enable_installer = 0


  10. [General]

  11. force_ssl = 0

  12. assume_secure_protocol = 1

  13. proxy_client_headers[] = "HTTP_X_FORWARDED_FOR"

  14. proxy_client_headers[] = "HTTP_X_ORIGINAL_FORWARDED_FOR"

  15. proxy_host_headers[] = "HTTP_X_FORWARDED_HOST"

  16. salt = "xxxx" // 加密串,用于解密配置内容

  17. trusted_hosts[] = "weread.qq.com"


  18. [Plugins]

  19. Plugins[] = "CorePluginsAdmin"

  20. // ...

  21. // 需要启动的插件列表,由于篇幅有限,省略默认的启动插件


  22. [PluginsInstalled]

  23. PluginsInstalled[] = "Diagnostics"

  24. // ...

  25. // 所有插件列表,,由于篇幅有限,省略默认的插件列表

复制出默认的配置文件后,即可根据业务进行修改,主要包括:

  • 数据库的配置,建议使用环境变量进行配置。

  • salt 是用于解密配置内容的加密串,保留默认配置中的值即可。

  • trusted_hosts[] 是部署 Matomo 的域名,支持多个域名配置,必须正确填写,否则无法使用。

  • Plugins[] 和 PluginsInstalled[] 分别是需要启用的插件和总插件列表,有需要调整插件的激活状态可以自行调整。

在修改完成后,可以利用 Docker 的命令把自定义的配置文件覆盖到镜像中,例如:

  1. # 复制配置文件

  2. COPY config.ini.php /var/www/html/config/config.ini.php

解决子目录部署的问题

Matomo 部署完成后,会以 weread.qq.com/weread-matomo 的形式去访问 Matomo 的服务,因此根据默认的 Apache2 配置,会尝试在 weread-matomo 这个目录中读取 Matomo,但实际上我们的 Matomo 是部署在根目录的,因此需要修改 Apache2 的配置文件,把针对 ^/weread-matomo 的访问指向根目录。

值得注意的是,出于安全考虑,我们不希望把 Matomo 的管理后台暴露到外网,因此在 Apache2 的配置中,可以通过正则指定只有追踪器相关的文件暴露到外网可以访问,方便业务引入。在了解了 Matomo 的源码后,追踪器相关的文件主要有 matomo.js 和 matomo.php,其中 matomo.php 结尾会带有参数,因此最终的 AliasMatch 规则如下:

  1. <VirtualHost *:80>

  2. ServerAdmin webmaster@localhost

  3. DocumentRoot /var/www/html


  4. ErrorLog ${APACHE_LOG_DIR}/error.log

  5. CustomLog ${APACHE_LOG_DIR}/access.log combined


  6. AliasMatch ^/weread-matomo/(matomo\.(js|php).*) /var/www/html/$1


  7. <Directory /var/www/html>

  8. Options All

  9. AllowOverride All

  10. order allow,deny

  11. allow from all

  12. </Directory>

  13. </VirtualHost>

社区镜像中 Apache2 的配置文件存放在 /etc/apache2/sites-available/000-default.conf,把上面的配置内容在本地保存一份 000-default.conf 后,在构建 Docker 镜像时利用命令覆盖默认的 Apache2 配置:

  1. COPY 000-default.conf /etc/apache2/sites-available/000-default.conf

无法显示城市信息

在解决了上面两个问题后,Matomo 的私有化部署基本已经跑通了,但后续我们发现,这样上报的数据中,并没有显示城市信息,Matomo 是基于 IP 信息来判定城市的,而 Matomo 自带的 IP 库仅能识别国家信息。

如上图所示,城市都显示为“未知”。为了解决这个问题,需要引入 IP 地址库,Matomo 支持 DBIP 和 GeoIP 2 两个外部的地址库,地址库的格式都是一种特殊的地址库 .mmdb 格式。

这里建议使用 GeoIP,在这里完成注册后,可以下载 .mmdb 格式的地址库。为了让 Matomo 识别出额外的地址库,需要把 .mmdb 放置到项目的 misc 目录,但由于 Matomo 使用了 Docker 部署,因此需要用 Docker 命令把 .mmdb 文件复制到容器的 misc 目录,最终完整的 Dockerfile 如下:

  1. # Dockerfile


  2. FROM matomo:latest


  3. MAINTAINER kayoli


  4. # 复制 Apache2 配置文件

  5. COPY 000-default.conf /etc/apache2/sites-available/000-default.conf


  6. # 复制 Matomo 配置文件

  7. COPY config.ini.php /var/www/html/config/config.ini.php


  8. # 引入 IP 地址库,用于显示 IP 对应的城市

  9. COPY mmdb/GeoLite2-ASN.mmdb /var/www/html/misc/GeoLite2-ASN.mmdb

  10. COPY mmdb/GeoLite2-City.mmdb /var/www/html/misc/GeoLite2-City.mmdb

  11. COPY mmdb/GeoLite2-Country.mmdb /var/www/html/misc/GeoLite2-Country.mmdb

前端引入追踪器也有坑

页面引入追踪器

经过上面的处理,已经解决了在 Docker 中部署服务端的问题,在 Matomo 的部署引导程序在完成后,会输出一段 JS 代码,用于给业务前端引入追踪器,例如:

  1. <!-- Matomo -->

  2. <script type="text/javascript">

  3. var _paq = window._paq = window._paq || [];

  4. /* tracker methods like "setCustomDimension" should be called before "trackPageView" */

  5. _paq.push(['trackPageView']);

  6. _paq.push(['enableLinkTracking']);

  7. (function() {

  8. var u="//xxxx"; // 私有化部署 Matomo 的域名

  9. _paq.push(['setTrackerUrl', u+'matomo.php']);

  10. _paq.push(['setSiteId', '1']);

  11. var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];

  12. g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);

  13. })();

  14. </script>

  15. <!-- End Matomo Code -->

Matomo 的追踪器包含了大量的选项和方法,主要包括:

  • Tracker Object,用于记录某个行为,例如上面代码中的 trackPageView 则用于记录某个页面被访问,enableLinkTracking 则是用于开启链接跳转时自动记录的功能。

  • Configuration of the Tracker Object,用于配置 Tracker Object,例如 setDocumentTitle 可以覆盖上报页面的标题,默认是获取 document.title。

  • Ecommerce,电商相关的方法,提供了一系列记录商品信息的方法。

  • Managing Consent,提供了一种机制来管理用户的跟踪上报。

具体可以参考文档。

自动记录 Vue SPA 的页面跳转

成功引入追踪器后发现,「内容管理平台」上报的用户行为,只有打开页面的操作,跳转页面并没有成功上报,但是默认的追踪器代码中,已经开启了 `` 选项。

在 Matomo 的源码中,可以看到对 `` 的说明:

  1. // @param bool enable Defaults to true.

  2. // If "true", use pseudo click-handler (treat middle click and open contextmenu as

  3. // left click). A right click (or any click that opens the context menu) on a link

  4. // will be tracked as clicked even if "Open in new tab" is not selected.

  5. // If "false" (default), nothing will be tracked on open context menu or middle click.

  6. // The context menu is usually opened to open a link / download in a new tab

  7. // therefore you can get more accurate results by treat it as a click but it can lead

  8. // to wrong click numbers.

  9. //

  10. this.enableLinkTracking = function (enable) {

  11. linkTrackingEnabled = true;

  12. // ...

  13. };

也就是说,这个选项仅对 link,也就是常见的 链接 这种形式的跳转才起作用,而「内容管理平台」是基于 Vue 开发的 Spa,页面跳转不是链接跳转,因此上报的记录里只有打开页面。

要解决这个问题,可以在 Vue 进行跳转时主动调用 Matomo 的上报,但实际上已经有开源的插件实现了这个,例如vue-matomo,具体使用可以参考它的使用文档。

值得注意的是,vue-matomo 对 matomo 的初始参数进行封装,除了文档中列出来的选项,其他选项在初始化的时候是无效的,可以在 vue-matomo 初始化后,通过 paq.push(['xxx']) 调用,paq 对象的 push 方法已经被重写,调用 push 方法实际上相当于把某个方法放入调用队列并进行调用。

Matomo 的最佳实践

经过上面的踩坑和填坑后,Matomo 最终得以在「内容管理平台」中落地投入使用,在经过一段时间的实践后,现有的自动记录还是不能满足我们的需求,例如我们需要自动上报 JS 错误信息,在点击 UI 元素时也需要上报,另外还需要在请求错误时进行自动上报。在经过一系列实践后,总结了一些最佳实践。

自动记录 JS 错误

在新版 Matomo 中,支持开启自动上报 JS 错误的功能,但功能尚未正式发布,因此官方文档中没有该功能的说明,需要调用的话可以通过 window._paq.push(['enableJSErrorTracking']); 开启该功能,为了保护 _paq 没有初始化好的情况,可以先判断 _paq 是否存在,例如:

  1. const enableJSErrorTracking = (): void => {

  2. if (window._paq) {

  3. window._paq.push(['enableJSErrorTracking']);

  4. } else {

  5. console.warn('can not found window._paq');

  6. }

  7. };

主动上报更多操作

除了链接跳转,页面中通常还会有一些 UI 操作不涉及链接变化,也不涉及请求,这类操作可以使用追踪器提供的 Tracker Object 进行主动上报,为了方便起见,可以抽取成工具方法,例如:

  1. // 上报一个事件,例如点击事件,播放事件等,在主动上报中比较常用。

  2. export const trackEvent = (category: string, action: string, name?: string, value?: number): void => {

  3. if (window._paq) {

  4. window._paq.push(['trackEvent', category, action, name, value]);

  5. } else {

  6. console.warn('can not found window._paq');

  7. }

  8. };


  9. // 二次封装,专门上报弹窗的动作,例如 action 参数可以填写 show, close

  10. export const trackDialogEvent = (action: string, name?: string, value?: number): void => {

  11. if (window._paq) {

  12. window._paq.push(['trackEvent', 'Dialog', action, name, value]);

  13. } else {

  14. console.warn('can not found window._paq');

  15. }

  16. };


  17. // 上报错误

  18. export const trackErrorEvent = (action: string, name?: string, value?: number): void => {

  19. if (window._paq) {

  20. window._paq.push(['trackEvent', 'Error', action, name, value]);

  21. } else {

  22. console.warn('can not found window._paq');

  23. }

  24. };


  25. // 上报搜索动作

  26. export const trackSiteSearch = (keyword: string, category?: string, resultsCount?: string): void => {

  27. if (window._paq) {

  28. window._paq.push(['trackSiteSearch', keyword, category, resultsCount]);

  29. } else {

  30. console.warn('can not found window._paq');

  31. }

  32. };

请求失败自动上报

业务中涉及请求的操作通常都比较关键,请求失败自动上报有利于记录下完整的用户动作路径,方便定位问题,在我们的业务中,我们的请求都是使用 Axios 发出的,因此可以利用 axios interceptors 劫持所有请求,在遇到指定错误时自动上报到 Matomo,例如:

  1. const baseURL = 'xxx';

  2. // axios instance

  3. const service = axios.create({

  4. baseURL,

  5. timeout: 60000,

  6. });


  7. service.interceptors.response.use(

  8. (response: AxiosResponse) => {

  9. const errCode = response.data && (response.data.errCode || response.data.errcode);

  10. if (errCode && errCode < 0) {

  11. const URL = response.config && response.config.url;

  12. const errMsg = response.data && (response.data.errMsg || response.data.errMsg);

  13. trackErrorEvent(URL, errMsg, errCode);

  14. }

  15. return response;

  16. },

  17. (error) => {

  18. return Promise.reject(error);

  19. }

  20. );

至此,已经可以很准确展示用户在访问页面时的完整操作路径了,开发者可以通过这些操作路径,结合业务日志,方便地去定位问题以及还原问题。

效果展示

经过以上的处理,现在已经可以上报非常丰富的访问数据,以及用户路径了,例如:

访客分析 - 访问日志

可以看到,界面上显示了完整的用户操作,通过时间轴的形式,配合不同的关键词和 icon 可以很好地呈现出实际的操作路径。

访客分析 - 设备

除了设备,在 Matomo 中还有地区等用户维度,并且 Matomo 在不同的数据展示中,例如目标转化率等,都可以基于这些不同的维度进行展示,对于分析用户组成相当方便。

转化与收益分析 - 概览

Matomo 支持设定指定的目标,用于计算转化率,并进行多个维度的展示,包括转化流向,每个阶段的转化人数和转化率等,并且可以通过不同的维度,例如渠道类型、城市、设备类型分别展示各种维度下的转化数据,这也是 Matomo 一种重要的特性,数据的展示维度丰富。

另外,Matomo 的插件机制也非常强大,可以插入自定义的数据,注入到各个界面或者基于 Matomo 自身收集的数据重新展示,基于篇幅所限,后续再对 Matomo 的插件机制进行实践说明。

Matomo 的性能分析与局限性

从上面的说明中可以看出,Matomo 的分析功能强大,分析的维度也很丰富,但同时也带来了较大的服务端资源消耗。

Matomo 的架构可以支撑千万级甚至亿级的月 PV,但同时对于服务端的 CPU,RAM 和硬盘空间都有相应的要求。因此在实际使用时,需要注意当前服务端的配置是否足以支撑上报页面的 PV 量,否则会导致 Matomo 无法及时处理数据甚至崩溃。

量化性能分析
  • Matomo 的默认配置是 1GB 内存,在默认配置下,Matomo 可以轻松支撑 1000 PV/天的访问量,对于这种级别的访问量,通常是一些内部平台或者面向特定人群的辅助页面。

  • 对于 3000 PV/天访问量的业务,则建议使用2核 CPU,2GB RAM,50GB 硬盘的配置。

  • 对于 30000 PV/天访问量的业务,则建议使用4核 CPU,8GB RAM,250GB 硬盘的配置,这个量级的访问可以是面向大众用户的业务页面了。

  • 对于 300000 PV/天访问量的业务,建议把 PHP 服务端和 MySQL 分开部署,对于这种量级的业务,MySQL 的瓶颈会更加明显,把 MySQL 部署进行单独部署,会更加稳定,建议最低的配置是8核 CPU,16GB RAM, 100GB 硬盘的机器作为 PHP 服务端,8核 CPU, 16GB RAM, 400GB 硬盘作为 MySQL 服务,。

  • 对于更高访问量的业务,可以再叠加机器配置,硬盘空间主页是给 MySQL 消耗用的,一个参考数据是:大概每增加500万PV,数据库就会增加1GB的数据。

可以看到,对于高访问量的业务,需要给予 Matomo 大量的服务器资源才能支撑,具体来说,超过 30000 PV/天的业务就需要注意了。因此在业务中引入时,可以考虑业务引入上报的必要性,如果页面功能单一,操作路径少,例如展示型的 H5 页面,其实引入 Matomo 的作用不是很大。

优化 Matomo 的性能的最佳实践

Matomo 对于 PHP 的最低版本要求是 PHP5,但尽量使用 PHP7,PHP7 在性能上有大量的优化。 使用 PHP cache,PHP5 及以上版本默认开启了。

通过调整 Innodb 配置来优化 MySQL 的性能,例如增加 innodbbufferpoolsize 来适应内存大小,另外可以把 innodbbufferpoolsize 设置为 MySQL 可用内存的80%。增大 innodbflushlogattrx_commit 来增加追踪器的吞吐量,具体可以参考这里。

业务访问量比较大的时候(例如 300000 PV/天的访问量),可以关闭实时动态处理的功能(管理 - 系统 - 通用设置 - 归档设置 - 在浏览器中查看报告时进行归档 - 否),关闭实时动态处理后,页面访问数据和用户访问轨迹也需要等待 cron 任务进行数据处理后才能展示,归档时间建议是设置为 3600 秒,减轻服务端的负担。

对于 URL 带 Query 的情况,如果无需要区分 Query 进行数据分析的情况,可以选择忽略这些 Query(管理 - 网站 - 管理 - 编辑网站 - 排除参数),否则同一个 URL 带有不同 Query,Matomo 会当作不同的 URL 来处理,大大增加 MySQL 的负担。

定期删除旧数据(管理 - 隐私设置 - 匿名化数据 - 定期删除旧的原始数据),旧数据的删除可以减小数据库的大小,既节省硬盘空间,也加快了数据的处理。

关于本文 作者:@Kayo 原文:https://tech.weread.qq.com/matomo-best-practices/

【招聘】广州微信读书团队招聘前端工程师和 IOS 客户端(专家)工程师


为你推荐


【第2207期】为git仓库自动生成changelog html页面


【第1366期】如何精确统计页面停留时长


欢迎自荐投稿,前端早读课等你来。

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存